package com.dabai.qrtools.utils;

import android.Manifest;
import android.annotation.TargetApi;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

import androidx.annotation.NonNull;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
import java.text.DecimalFormat;
import java.util.ArrayList;

/**
 * IO操作文件工具
 */
public class FileUtils {


    /**
     * 重命名
     *
     * @param file           文件
     * @param name           新的名字
     * @param containsSuffix 修改时包含后缀
     * @return boolean
     */
    public static boolean rename(File file, String name, boolean containsSuffix) {
        if (file == null || StringUtils.isEmpty(name)){
            return false;
        }

        String socName = file.getName();

        if (containsSuffix) {

            File oldFile = new File(file.getParentFile(), socName);
            File newFile = new File(file.getParentFile(), name);
            return oldFile.renameTo(newFile);
        }else {

            String suffix = socName.substring(socName.lastIndexOf("."));

            File oldFile = new File(file.getParentFile(), socName);
            File newFile = new File(file.getParentFile(), name+suffix);

            return oldFile.renameTo(newFile);
        }

    }


    /**
     * 扫描这个目录 以及 子目录 里所有文件 并 按照后缀名扫描
     *
     * @param dir    dir
     * @param suffix 后缀
     */
    public static ArrayList<File> scanFilesWithSuffix(@NonNull File dir, @NonNull String suffix) {

        //储存文件
        ArrayList<File> fileList = new ArrayList<>();

        File[] files = dir.listFiles();

        if (files == null) {
            return fileList;
        }

        for (File file : files) {

            if (file.isFile()) {
                if (file.getName().endsWith(suffix)) {
                    fileList.add(file);
                }
            } else if (file.isDirectory()) {
                scanDir(file, fileList, suffix);
            }
        }

        return fileList;
    }


    public static void scanDir(File dir, ArrayList<File> fileList, @NonNull String suffix) {
        File[] files = dir.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                scanDir(file, fileList, suffix);
            } else {
                if (file.getName().endsWith(suffix)) {
                    fileList.add(file);
                }
            }
        }
    }


    /**
     * 得到dir 如果没有目录 会创建
     *
     * @param file 文件
     * @return {@link File}
     */
    public static File getDir(File file) {
        if (!file.exists()) {
            file.mkdirs();
        }
        return file;
    }


    /**
     * Android10以下 - 直接保存bitmap到本地
     * 此方法只适用于储存一些缓存图片
     *
     * @param bitmap Bitmap
     */
    public static boolean saveBitmap(Bitmap bitmap, String path) {
        String savePath;
        File filePic;
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            savePath = path;
        } else {
            return false;
        }
        try {
            filePic = new File(savePath);
            if (!filePic.exists()) {
                filePic.getParentFile().mkdirs();
                filePic.createNewFile();
            }
            FileOutputStream fos = new FileOutputStream(filePic);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.flush();
            fos.close();
            return true;
        } catch (IOException e) {
            return false;
        }
    }


    /**
     * 保存位图到图库 适配Android10
     * <p>
     * 需要注意的是 Android10以上不需要权限,以下需要储存权限
     * <p>
     * 会保存到 DCIM/dirName
     *
     * @param context  上下文
     * @param bitmap   位图
     * @param fileName 文件名称
     * @return boolean
     */
    public static boolean saveBitmapToDCIM(Context context, Bitmap bitmap, String dirName, String fileName) {
        System.out.println("Android系统 " + Build.VERSION.RELEASE + " 保存图片到 " + dirName);

        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
        contentValues.put(MediaStore.Images.Media.DESCRIPTION, fileName);
        //兼容Android Q
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/" + dirName);
            contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
            Uri uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
            OutputStream outputStream;
            try {
                outputStream = context.getContentResolver().openOutputStream(uri);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
                outputStream.flush();
                outputStream.close();
                notifyGallery(context, uri);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }

        } else {
            if (DabaiUtils.isPermissionChecked(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                System.out.println("WRITE_EXTERNAL_STORAGE 有权限");
                File photoFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/" + dirName, fileName + ".jpg");
                boolean isOk = saveBitmap(bitmap, photoFile.getAbsolutePath());
                notifyGallery(context, photoFile);
                return isOk;
            } else {
                System.out.println("WRITE_EXTERNAL_STORAGE 无权限");
                return false;
            }
        }
    }


    /**
     * 通知画廊
     *
     * @param context 上下文
     * @param file    文件
     */
    public static void notifyGallery(Context context, File file) {
        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
    }

    /**
     * 通知画廊
     *
     * @param context 上下文
     * @param uri     uri
     */
    public static void notifyGallery(Context context, Uri uri) {
        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
    }


    /**
     * 删除文件/文件夹
     *
     * @param filePath 文件路径
     * @return boolean
     */
    public static boolean delete(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            System.out.println("删除文件失败:" + filePath + "不存在！");
            return false;
        } else {
            if (file.isFile()) {
                return deleteFile(filePath);
            } else {
                return deleteDirectory(filePath);
            }
        }
    }

    /**
     * 删除文件/文件夹
     *
     * @param file 文件
     * @return boolean
     */
    public static boolean delete(File file) {
        return delete(file.getAbsolutePath());
    }

    /**
     * 删除单个文件
     *
     * @param fileName 要删除的文件的文件名
     * @return 单个文件删除成功返回true，否则返回false
     */
    private static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        // 如果文件路径所对应的文件存在，并且是一个文件，则直接删除
        if (file.exists() && file.isFile()) {
            if (file.delete()) {
                System.out.println("删除单个文件" + fileName + "成功！");
                return true;
            } else {
                System.out.println("删除单个文件" + fileName + "失败！");
                return false;
            }
        } else {
            System.out.println("删除单个文件失败：" + fileName + "不存在！");
            return false;
        }
    }

    /**
     * 删除目录及目录下的文件
     *
     * @param dir 要删除的目录的文件路径
     * @return 目录删除成功返回true，否则返回false
     */
    private static boolean deleteDirectory(String dir) {
        // 如果dir不以文件分隔符结尾，自动添加文件分隔符
        if (!dir.endsWith(File.separator)) {
            dir = dir + File.separator;
        }
        File dirFile = new File(dir);
        // 如果dir对应的文件不存在，或者不是一个目录，则退出
        if ((!dirFile.exists()) || (!dirFile.isDirectory())) {
            System.out.println("删除目录失败：" + dir + "不存在！");
            return false;
        }
        boolean flag = true;
        // 删除文件夹中的所有文件包括子目录
        File[] files = dirFile.listFiles();
        assert files != null;
        for (File file : files) {
            // 删除子文件
            if (file.isFile()) {
                flag = deleteFile(file.getAbsolutePath());
                if (!flag) {
                    break;
                }
            }
            // 删除子目录
            else if (file.isDirectory()) {
                flag = deleteDirectory(file.getAbsolutePath());
                if (!flag) {
                    break;
                }
            }
        }
        if (!flag) {
            System.out.println("删除目录失败！");
            return false;
        }
        // 删除当前目录
        if (dirFile.delete()) {
            System.out.println("删除目录" + dir + "成功！");
            return true;
        } else {
            return false;
        }
    }


    /**
     * 删除目录及目录下的文件
     *
     * @param dir 要删除的目录的文件路径
     * @return 目录删除成功返回true，否则返回false
     */
    private static boolean deleteDirectory(String dir, boolean deleteCurrentDirectory) {
        // 如果dir不以文件分隔符结尾，自动添加文件分隔符
        if (!dir.endsWith(File.separator)) {
            dir = dir + File.separator;
        }
        File dirFile = new File(dir);
        // 如果dir对应的文件不存在，或者不是一个目录，则退出
        if ((!dirFile.exists()) || (!dirFile.isDirectory())) {
            System.out.println("删除目录失败：" + dir + "不存在！");
            return false;
        }
        boolean flag = true;
        // 删除文件夹中的所有文件包括子目录
        File[] files = dirFile.listFiles();
        assert files != null;
        for (File file : files) {
            // 删除子文件
            if (file.isFile()) {
                flag = deleteFile(file.getAbsolutePath());
                if (!flag) {
                    break;
                }
            }
            // 删除子目录
            else if (file.isDirectory()) {
                flag = deleteDirectory(file.getAbsolutePath());
                if (!flag) {
                    break;
                }
            }
        }
        if (!flag) {
            System.out.println("删除目录失败！");
            return false;
        }

        if (deleteCurrentDirectory) {
            // 删除当前目录
            if (dirFile.delete()) {
                System.out.println("删除目录" + dir + "成功！");
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }

    }


    /**
     * 写文本
     *
     * @param filePath 文件路径
     * @param content  内容
     * @return boolean
     */
    public static boolean writeText(String filePath, String content) {
        try {

            File file = new File(filePath);

            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }

            if (!file.exists()) {
                file.createNewFile();
            }

            FileWriter fw = new FileWriter(file.getAbsoluteFile());
            BufferedWriter bw = new BufferedWriter(fw);
            bw.write(content);
            bw.close();
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * 写文本
     *
     * @param file    文件
     * @param content 内容
     * @return boolean
     */
    public static boolean writeText(File file, String content) {
        try {
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fw = new FileWriter(file.getAbsoluteFile());
            BufferedWriter bw = new BufferedWriter(fw);
            bw.write(content);
            bw.close();
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * 阅读文本
     *
     * @param filePath 文件路径
     * @return 内容
     */
    public static String readText(String filePath) {
        File file = new File(filePath);
        StringBuilder stringBuilder = null;

        try {
            stringBuilder = new StringBuilder();
            FileReader fileReader = new FileReader(filePath);
            int length = 0;
            while ((length = fileReader.read()) != -1) {
                stringBuilder.append((char) length);
            }
            fileReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return stringBuilder.toString();
    }

    /**
     * 阅读文本
     *
     * @param file 文件
     * @return {@link String}
     */
    public static String readText(File file) {

        StringBuilder stringBuilder = null;

        try {
            stringBuilder = new StringBuilder();
            FileReader fileReader = new FileReader(file);
            int length = 0;
            while ((length = fileReader.read()) != -1) {
                stringBuilder.append((char) length);
            }
            fileReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return stringBuilder.toString();
    }

    /**
     * 复制文件
     *
     * @param source    源文件
     * @param dest      目标文件
     * @param coverCopy 覆盖复制
     * @return boolean
     */
    public static boolean copyFile(File source, File dest, boolean coverCopy) {

        //是否覆盖复制
        if (!coverCopy) {
            if (dest.exists()) {
                return false;
            }
        }

        try {
            if (!source.exists()) {
                return false;
            }
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            FileChannel inputChannel = null;
            FileChannel outputChannel = null;
            try {
                inputChannel = new FileInputStream(source).getChannel();
                outputChannel = new FileOutputStream(dest).getChannel();
                outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
                return true;
            } finally {
                if (inputChannel != null) inputChannel.close();
                if (outputChannel != null) outputChannel.close();
            }
        } catch (IOException e) {
            System.out.println("复制失败:"+e.getMessage());
            return false;
        }
    }

    /**
     * 复制文件夹
     *
     * @param source    源文件夹路径
     * @param dest      目标文件夹路径
     * @param coverCopy 覆盖复制文件
     * @throws Exception 异常
     */
    public static void copyDir(String source, String dest, boolean coverCopy) throws Exception {
        File path1 = new File(source);
        File path2 = new File(dest);

        // 如果源目录不存在,那就不用复制了,
        if (path1.exists()) {
            // Create destination folder,如果目标目录不存在,就创建目标目录,因为没有目录文件复制不过去的
            if (!path2.exists()) {
                path2.mkdirs();
            }

            // 取得源目录下面的所有文件和文件夹
            File[] items = path1.listFiles();

            // 取得所有文件和文件夹之后,遍历处理,如果是文件,就复制文件,如果是文件夹,则递归文件夹下面的文件和文件夹

            for (File item : items) {
                // 如果是文件才进行复制
                if (item.isFile()) {
                    // 输入输出流的两个常用构造函数,其中在用来了一个字段File.separator,先用输入流读取文件,然后用输出流写文件到目标位置,完成复制功能
                    copyFile(item, new File(path2 + File.separator + item.getName()), coverCopy);
                }
                // 如果是文件夹,递归文件夹
                else {
                    copyDir(item.getPath(), path2 + File.separator + item.getName(), coverCopy);
                }
            }
        } else {
            throw new Exception("源路径：[" + source + "] 不存在...");
        }
    }

    /**
     * 获取文件大小的字符串
     * 获取 带单位的文件大小字符串
     * 方法会自动识别 文件或文件夹
     * 单位 会 自动格式化
     *
     * @param file 文件
     * @return 字符串
     */
    public static String getFileSizeToString(File file) {

        if (file.isDirectory()) {
            return formatFileSizeToString(getFolderSize(file));
        } else {
            return formatFileSizeToString(file.length());
        }

    }

    /**
     * 获取文件大小
     * 单位为 Byte ( B )
     *
     * @param file 文件
     * @return long
     */
    public static long getFileSize(File file) {
        if (file.isDirectory()) {
            return getFolderSize(file);
        } else {
            return file.length();
        }
    }

    /**
     * 获取文件夹大小
     *
     * @param file 文件
     * @return long
     */
    private static long getFolderSize(File file) {
        long size = 0;
        try {
            File[] fileList = file.listFiles();
            for (int i = 0; i < fileList.length; i++) {
                // 如果下面还有文件
                if (fileList[i].isDirectory()) {
                    size = size + getFolderSize(fileList[i]);
                } else {
                    size = size + fileList[i].length();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return size;
    }

    /**
     * 格式字符串的文件大小
     * 格式化文件长度大小
     * 把long型值转化成带单位的字符串
     *
     * @param fileSize 文件大小
     * @return 字符串
     */
    public static String formatFileSizeToString(long fileSize) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        if (fileSize <= 0) {
            fileSizeString = "0B";
        } else if (fileSize < 1024) {
            fileSizeString = df.format((double) fileSize) + "B";
        } else if (fileSize < 1048576) {
            fileSizeString = df.format((double) fileSize / 1024) + "KB";
        } else if (fileSize < 1073741824) {
            fileSizeString = df.format((double) fileSize / 1048576) + "MB";
        } else {
            fileSizeString = df.format((double) fileSize / 1073741824) + "GB";
        }
        return fileSizeString;
    }


    /**
     * uri转真实路径
     *
     * @param context 上下文
     * @param uri     uri
     * @return {@link String}
     */
    @Deprecated
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static String UriToPath(final Context context, final Uri uri) {

        // DocumentProvider
        if (DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
//                Log.i(TAG,"isDownloadsDocument***"+uri.toString());
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.parseLong(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
//                Log.i(TAG,"isMediaDocument***"+uri.toString());
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[]{split[1]};

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
//            Log.i(TAG,"content***"+uri.toString());
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
//            Log.i(TAG,"file***"+uri.toString());
            return uri.getPath();
        }
        return null;
    }

    private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {column};

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return null;
    }

    /**
     * 是外部存储文档
     *
     * @param uri uri
     * @return boolean
     */
    private static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * 是下载文档
     *
     * @param uri uri
     * @return boolean
     */
    private static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * 是媒体文件
     *
     * @param uri uri
     * @return boolean
     */
    private static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }


}
